package com.yanqu.xiuxian.crosssystem.manger.elementalbonds.logic;

import com.yanqu.xiuxian.config.elementalbonds.ElementalBondsConfigMgr;
import com.yanqu.xiuxian.config.elementalbonds.ElementalBondsParam;
import com.yanqu.xiuxian.config.elementalbonds.enums.*;
import com.yanqu.xiuxian.crosssystem.manger.elementalbonds.data.ElementalBondsGameData;
import com.yanqu.xiuxian.crosssystem.manger.elementalbonds.entity.ElementalBondsBlock;
import com.yanqu.xiuxian.crosssystem.manger.elementalbonds.entity.ElementalBondsBomb;
import com.yanqu.xiuxian.crosssystem.manger.elementalbonds.entity.ElementalBondsOperateEffect;
import com.yanqu.xiuxian.crosssystem.manger.elementalbonds.entity.eliminate.ElementalBondsEliminateTip;
import com.yanqu.xiuxian.crosssystem.manger.elementalbonds.param.ElementalBondsCardParam;
import com.yanqu.xiuxian.crosssystem.manger.elementalbonds.param.ElementalBondsMoveParam;
import com.yanqu.xiuxian.crosssystem.manger.elementalbonds.param.ElementalBondsSkillParam;
import com.yanqu.xiuxian.crosssystem.manger.elementalbonds.result.ElementalBondsOperateResult;
import com.yanqu.xiuxian.crosssystem.manger.elementalbonds.result.ElementalBondsOperateRoundResult;
import com.yanqu.xiuxian.server.protocol.GameErrorCode;
import com.yanqu.xiuxian.utils.RandomHelper;

import java.util.*;

public class ElementalBondsBlockLogic {

    public static void checkRearrange(ElementalBondsGameData elementalBondsGameData, ElementalBondsOperateResult elementalBondsOperateResult, Random random) {
        ElementalBondsEliminateTip elementalBondsEliminateTip = ElementalBondsEliminateLogic.getEliminateTip(elementalBondsGameData);
        if (elementalBondsEliminateTip == null) {
            // 重排
            rearrange(elementalBondsGameData, random);

            // 添加重排结果
            elementalBondsOperateResult.addElementalBondsOperateRoundResult(new ElementalBondsOperateRoundResult(true, elementalBondsGameData.clone()));
        }
    }

    public static void rearrange(ElementalBondsGameData elementalBondsGameData, Random random) {
        List<List<ElementalBondsBlock>> backupBoardBlockList = elementalBondsGameData.clone().getBlockList();
        // 重排10个结果，取最差的结果，或者无法消除直接取那个结果
        int times = 10;
        List<List<ElementalBondsBlock>> worstResultBlockList = null;
        int eliminateNum = 0;

        // 多次重排
        for (int i = 0; i < 100; i++) {

            // 恢复备份盘面
            elementalBondsGameData.setBlockList(backupBoardBlockList);
            List<List<ElementalBondsBlock>> blockList = elementalBondsGameData.getBlockList();

            // 打乱格子
            List<ElementalBondsBlock> allElementalBondsBlockList = new ArrayList<>();
            for (List<ElementalBondsBlock> elementalBondsBlockList : blockList) {
                for (ElementalBondsBlock elementalBondsBlock : elementalBondsBlockList) {
                    if (elementalBondsBlock.getElementalBondsBlockType() == ElementalBondsBlockType.BAI_BAO_XIANG
                            || elementalBondsBlock.getElementalBondsBlockType() == ElementalBondsBlockType.SMART_BOX) {
                        continue; // 跳过百宝箱、机巧盒
                    }
                    allElementalBondsBlockList.add(elementalBondsBlock);
                }
            }
            Collections.shuffle(allElementalBondsBlockList, random);

            // 重排
            int index = 0;
            for (int y = 0; y < blockList.size(); y++) {
                for (int x = 0; x < blockList.get(y).size(); x++) {
                    ElementalBondsBlock oldElementalBondsBlock = blockList.get(y).get(x);
                    if (oldElementalBondsBlock.getElementalBondsBlockType() == ElementalBondsBlockType.BAI_BAO_XIANG
                            || oldElementalBondsBlock.getElementalBondsBlockType() == ElementalBondsBlockType.SMART_BOX) {
                        continue; // 跳过百宝箱
                    }
                    ElementalBondsBlock elementalBondsBlock = allElementalBondsBlockList.get(index++).clone();
                    elementalBondsBlock.setX(x);
                    elementalBondsBlock.setY(y);
                    blockList.get(y).set(x, elementalBondsBlock);
                }
            }

            // 重排后还是无法消除
            if (ElementalBondsEliminateLogic.getEliminateTip(elementalBondsGameData) == null) {
                continue;
            }

            // 比较直接消除数量
            Set<ElementalBondsBlock> elementalBondsBlockSet = new HashSet<>();
            ElementalBondsEliminateLogic.handleSameColorEliminate(blockList, elementalBondsBlockSet);
            if (worstResultBlockList == null || elementalBondsBlockSet.size() < eliminateNum) {
                worstResultBlockList = elementalBondsGameData.getBlockList();
                eliminateNum = elementalBondsBlockSet.size();
            }

            // 次数判断
            if (--times <= 0 || eliminateNum == 0) {
                break;
            }
        }

        // 替换原来格子
        elementalBondsGameData.setBlockList(worstResultBlockList);
    }

    public static boolean exist(List<List<ElementalBondsBlock>> blockList, int x, int y) {
        if (y < 0 || y >= blockList.size()) {
            return false;
        }
        if (x < 0 || x >= blockList.get(y).size()) {
            return false;
        }
        return blockList.get(y).get(x) != null;
    }

    public static boolean exist(List<List<ElementalBondsBlock>> blockList, ElementalBondsCardParam elementalBondsCardParam) {
        boolean fromExist = exist(blockList, elementalBondsCardParam.getFromX(), elementalBondsCardParam.getFromY());
        boolean toExist = exist(blockList, elementalBondsCardParam.getToX(), elementalBondsCardParam.getToY());
        return fromExist && toExist;
    }

    public static boolean exist(List<List<ElementalBondsBlock>> blockList, ElementalBondsMoveParam elementalBondsMoveParam) {
        boolean fromExist = exist(blockList, elementalBondsMoveParam.getFromX(), elementalBondsMoveParam.getFromY());
        boolean toExist = exist(blockList, elementalBondsMoveParam.getToX(), elementalBondsMoveParam.getToY());
        return fromExist && toExist;
    }

    public static boolean exist(List<List<ElementalBondsBlock>> blockList, ElementalBondsSkillParam elementalBondsSkillParam) {
        boolean fromExist = exist(blockList, elementalBondsSkillParam.getFromX(), elementalBondsSkillParam.getFromY());
        boolean toExist = exist(blockList, elementalBondsSkillParam.getToX(), elementalBondsSkillParam.getToY());
        return fromExist && toExist;
    }

    public static int checkExchange(List<List<ElementalBondsBlock>> blockList, ElementalBondsMoveParam elementalBondsMoveParam) {
        if (!exist(blockList, elementalBondsMoveParam)) {
            return GameErrorCode.E_ELEMENTAL_BONDS_POS_NO_EXIST;
        }
        int subX = Math.abs(elementalBondsMoveParam.getFromX() - elementalBondsMoveParam.getToX());
        int subY = Math.abs(elementalBondsMoveParam.getFromY() - elementalBondsMoveParam.getToY());
        if (subX + subY != 1) {
            return GameErrorCode.E_ELEMENTAL_BONDS_POS_CAN_NOT_MOVE;
        }
        return GameErrorCode.E_SUCCESS_VALUE;
    }

    public static void exchange(List<List<ElementalBondsBlock>> blockList, ElementalBondsCardParam elementalBondsCardParam, ElementalBondsOperateResult elementalBondsOperateResult) {
        exchange(blockList, elementalBondsCardParam.getFromX(), elementalBondsCardParam.getFromY(), elementalBondsCardParam.getToX(), elementalBondsCardParam.getToY(), elementalBondsOperateResult);
    }

    public static void exchange(List<List<ElementalBondsBlock>> blockList, ElementalBondsMoveParam elementalBondsMoveParam, ElementalBondsOperateResult elementalBondsOperateResult) {
        exchange(blockList, elementalBondsMoveParam.getFromX(), elementalBondsMoveParam.getFromY(), elementalBondsMoveParam.getToX(), elementalBondsMoveParam.getToY(), elementalBondsOperateResult);
    }

    public static void exchange(List<List<ElementalBondsBlock>> blockList, ElementalBondsSkillParam elementalBondsSkillParam, ElementalBondsOperateResult elementalBondsOperateResult) {
        exchange(blockList, elementalBondsSkillParam.getFromX(), elementalBondsSkillParam.getFromY(), elementalBondsSkillParam.getToX(), elementalBondsSkillParam.getToY(), elementalBondsOperateResult);
    }

    public static void exchange(List<List<ElementalBondsBlock>> blockList, int fromX, int fromY, int toX, int toY, ElementalBondsOperateResult elementalBondsOperateResult) {
        ElementalBondsBlock fromElementalBondsBlock = blockList.get(fromY).get(fromX);
        ElementalBondsBlock toElementalBondsBlock = blockList.get(toY).get(toX);

        fromElementalBondsBlock.setX(toX);
        fromElementalBondsBlock.setY(toY);
        toElementalBondsBlock.setX(fromX);
        toElementalBondsBlock.setY(fromY);

        blockList.get(fromY).set(fromX, toElementalBondsBlock);
        blockList.get(toY).set(toX, fromElementalBondsBlock);

        if (elementalBondsOperateResult != null) {
            boolean isMerge = combBombPart(fromElementalBondsBlock) && combBombPart(toElementalBondsBlock);
            ElementalBondsOperateEffect elementalBondsOperateEffect = new ElementalBondsOperateEffect(isMerge ? ElementalBondsEffectType.MERGE_BLOCK : ElementalBondsEffectType.EXCHANGE_BLOCK);
            elementalBondsOperateEffect.addElementalBondsBlock(toElementalBondsBlock);
            elementalBondsOperateEffect.addElementalBondsBlock(fromElementalBondsBlock);
            elementalBondsOperateResult.addElementalBondsOperateEffect(elementalBondsOperateEffect);
        }
    }

    public static boolean combBombPart(ElementalBondsBlock elementalBondsBlock) {
        return elementalBondsBlock.getElementalBondsBlockType().getBigType() == ElementalBondsBlockBigType.NORMAL_BOMB;
    }

    public static int getPos(List<List<ElementalBondsBlock>> blockList, ElementalBondsBlock elementalBondsBlock) {
        return elementalBondsBlock.getY() * blockList.get(0).size() + elementalBondsBlock.getX();
    }

    public static ElementalBondsBlock getBlock(List<List<ElementalBondsBlock>> blockList, int pos) {
        int size = blockList.get(0).size();
        int y = pos / size;
        int x = pos % size;
        return blockList.get(y).get(x);
    }

    public static List<ElementalBondsBlock> getSameColorList(List<List<ElementalBondsBlock>> blockList, ElementalBondsBlock elementalBondsBlock) {
        List<ElementalBondsBlock> lineElementalBondsBlockList = new ArrayList<>();
        lineElementalBondsBlockList.add(elementalBondsBlock);
        List<ElementalBondsBlock> columnElementalBondsBlockList = new ArrayList<>();
        columnElementalBondsBlockList.add(elementalBondsBlock);

        // 上
        for (int y = elementalBondsBlock.getY() + 1; y < blockList.size(); y++) {
            if (exist(blockList, elementalBondsBlock.getX(), y) && blockList.get(y).get(elementalBondsBlock.getX()).getColor() == elementalBondsBlock.getColor()) {
                columnElementalBondsBlockList.add(blockList.get(y).get(elementalBondsBlock.getX()));
            } else break;
        }
        // 下
        for (int y = elementalBondsBlock.getY() - 1; y >= 0; y--) {
            if (exist(blockList, elementalBondsBlock.getX(), y) && blockList.get(y).get(elementalBondsBlock.getX()).getColor() == elementalBondsBlock.getColor()) {
                columnElementalBondsBlockList.add(blockList.get(y).get(elementalBondsBlock.getX()));
            } else break;
        }
        // 左
        for (int x = elementalBondsBlock.getX() - 1; x >= 0; x--) {
            if (exist(blockList, x, elementalBondsBlock.getY()) && blockList.get(elementalBondsBlock.getY()).get(x).getColor() == elementalBondsBlock.getColor()) {
                lineElementalBondsBlockList.add(blockList.get(elementalBondsBlock.getY()).get(x));
            } else break;
        }
        // 右
        for (int x = elementalBondsBlock.getX() + 1; x < blockList.get(elementalBondsBlock.getY()).size(); x++) {
            if (exist(blockList, x, elementalBondsBlock.getY()) && blockList.get(elementalBondsBlock.getY()).get(x).getColor() == elementalBondsBlock.getColor()) {
                lineElementalBondsBlockList.add(blockList.get(elementalBondsBlock.getY()).get(x));
            } else break;
        }

        if (lineElementalBondsBlockList.size() >= 3 && columnElementalBondsBlockList.size() >= 3) {
            columnElementalBondsBlockList.remove(0);
            lineElementalBondsBlockList.addAll(columnElementalBondsBlockList);
            return lineElementalBondsBlockList;
        } else if (lineElementalBondsBlockList.size() >= 3) {
            return lineElementalBondsBlockList;
        } else if (columnElementalBondsBlockList.size() >= 3) {
            return columnElementalBondsBlockList;
        } else {
            return Collections.emptyList();
        }
    }

    public static void removeNoBaseFourBigType(Set<ElementalBondsBlock> elementalBondsBlockSet) {
        elementalBondsBlockSet.removeIf(elementalBondsBlock -> !elementalBondsBlock.getElementalBondsBlockType().getBigType().isBase());
    }

    public static ElementalBondsBlock getSmartBoxBlock(List<List<ElementalBondsBlock>> blockList) {
        for (List<ElementalBondsBlock> elementalBondsBlockList : blockList) {
            for (ElementalBondsBlock elementalBondsBlock : elementalBondsBlockList) {
                if (elementalBondsBlock.getElementalBondsBlockType() == ElementalBondsBlockType.SMART_BOX) {
                    return elementalBondsBlock;
                }
            }
        }
        return null;
    }

    public static Set<ElementalBondsBlock> drop(List<List<ElementalBondsBlock>> blockList, ElementalBondsGameData elementalBondsGameData,
                                                Random random, int guideDropNo, int roomRound, int turnPlayer) {

        Set<ElementalBondsBlock> dropElementalBondsBlockSet = new HashSet<>();

        // 旧方块掉落
        for (int y = 0; y < blockList.size(); y++) {
            for (int x = 0; x < blockList.get(y).size(); x++) {
                if (blockList.get(y).get(x) == null) {
                    // 找他上面的格子掉落
                    for (int upY = y + 1; upY < blockList.size(); upY++) {
                        if (blockList.get(upY).get(x) != null) {
                            ElementalBondsBlock upElementalBondsBlock = blockList.get(upY).get(x).clone(); // 复制一个出来

                            upElementalBondsBlock.setY(y);
                            blockList.get(y).set(x, upElementalBondsBlock);
                            blockList.get(upY).set(x, null);

                            dropElementalBondsBlockSet.add(upElementalBondsBlock);
                            break;
                        }
                    }
                }
            }
        }

        // 统计方块数量
        int emptyNum = 0;
        Map<Integer, Integer> colorCountMap = new HashMap<>();
        for (List<ElementalBondsBlock> elementalBondsBlockList : blockList) {
            for (ElementalBondsBlock elementalBondsBlock : elementalBondsBlockList) {
                if (elementalBondsBlock != null) {
                    int num = colorCountMap.getOrDefault(elementalBondsBlock.getColor(), 0);
                    colorCountMap.put(elementalBondsBlock.getColor(), num + 1);
                } else {
                    emptyNum++;
                }
            }
        }

        // 准备掉落方块
        List<Integer> dropColorList = new ArrayList<>();
        int colorNum = ElementalBondsConfigMgr.getInstance().getColorNum();
        if (roomRound >= ElementalBondsParam.getInstance().getDrop2OpenRound()) {
            // 【新掉落机制】
            List<Integer> drop2List = ElementalBondsParam.getInstance().getDrop2List();
            int blueColor = turnPlayer == 1 ? ElementalBondsColorType.RED.getType() : ElementalBondsColorType.BLUE.getType();
            for (int i = 0; i < emptyNum; i++) {
                List<Integer> weightList = new ArrayList<>();
                for (int color = 1; color <= colorNum; color++) {
                    int weight = colorCountMap.getOrDefault(color, 0) + drop2List.get(0);           // 当前数量+a
                    if (color == blueColor) {
                        weight += drop2List.get(1);                                                              // +b
                        weight *= drop2List.get(3);                                                              // *c2
                    } else {
                        weight *= drop2List.get(2);                                                              // *c1
                    }
                    weightList.add(weight);
                }
                int index = RandomHelper.getRandomIndexByWeight(weightList, random);
                int color = index + 1;
                dropColorList.add(color);
                colorCountMap.put(color, 1 + colorCountMap.getOrDefault(color, 0));
            }
        } else {
            // 【旧掉落机制】
            for (int i = 0; i < emptyNum; i++) {
                List<Integer> weightList = new ArrayList<>();
                for (int j = 0; j < colorNum; j++) {
                    weightList.add(ElementalBondsParam.getInstance().getDropBaseWeight());
                }
                for (Map.Entry<Integer, Integer> entry : colorCountMap.entrySet()) {
                    if (entry.getValue() < ElementalBondsParam.getInstance().getDropAddiCountLimit()) {
                        if (entry.getKey() == ElementalBondsColorType.NONE.getType()) {
                            continue;
                        }
                        int colorIndex = entry.getKey() - 1;
                        weightList.set(colorIndex, weightList.get(colorIndex) + ElementalBondsParam.getInstance().getDropAddiWeight());
                    }
                }
                int index = RandomHelper.getRandomIndexByWeight(weightList, random);
                int color = index + 1;
                dropColorList.add(color);
                colorCountMap.put(color, 1 + colorCountMap.getOrDefault(color, 0));
            }
        }
        Collections.shuffle(dropColorList, random);

        // 新手引导替换下掉落方块
        if (guideDropNo > 0) {
            List<Integer> tmpList = ElementalBondsGuideLogic.getDropColorList(guideDropNo);
            for (int i = 0; i < dropColorList.size() && i < tmpList.size(); i++) {
                dropColorList.set(i, tmpList.get(i));
            }
        }

        // 掉落新方块
        int index = 0;
        List<ElementalBondsBlock> newDropElementalBondsBlockList = new ArrayList<>();
        for (int y = 0; y < blockList.size(); y++) {
            for (int x = 0; x < blockList.get(y).size(); x++) {
                if (blockList.get(y).get(x) == null) {
                    int color = index < dropColorList.size() ? dropColorList.get(index) : random.nextInt(colorNum) + 1;
                    index++;

                    // 如果可以消除有概率要换个颜色
                    if (exist(blockList, x - 1, y) && blockList.get(y).get(x - 1).getColor() == color &&
                            exist(blockList, x - 2, y) && blockList.get(y).get(x - 2).getColor() == color ||
                            exist(blockList, x + 1, y) && blockList.get(y).get(x + 1).getColor() == color &&
                                    exist(blockList, x + 2, y) && blockList.get(y).get(x + 2).getColor() == color ||
                            exist(blockList, x, y - 1) && blockList.get(y - 1).get(x).getColor() == color &&
                                    exist(blockList, x, y - 2) && blockList.get(y - 2).get(x).getColor() == color) {
                        if (random.nextInt(1000) < ElementalBondsParam.getInstance().getDropStableWeight()) {
                            int newColor = random.nextInt(colorNum - 1) + 1; // 少取一种颜色
                            if (newColor == color) {
                                newColor = colorNum; // 直接最后一种颜色
                            }
                            color = newColor;
                        }
                    }

                    ElementalBondsBlock elementalBondsBlock = new ElementalBondsBlock(x, y, color);
                    ElementalBondsSceneLogic.scene_12_checkEffectChange(elementalBondsBlock, elementalBondsGameData);
                    blockList.get(y).set(x, elementalBondsBlock);

                    dropElementalBondsBlockSet.add(elementalBondsBlock);
                    newDropElementalBondsBlockList.add(elementalBondsBlock);
                }
            }
        }

        // 盘面机制掉落替换
        ElementalBondsSceneLogic.replaceDropBlock(random, elementalBondsGameData, newDropElementalBondsBlockList);

        return dropElementalBondsBlockSet;
    }

    public static Set<ElementalBondsBlock> getCanBoomBlock(List<ElementalBondsBomb> elementalBondsBombList, Set<ElementalBondsBlock> elementalBondsBlockSet) {
        Set<ElementalBondsBlock> canBoomBlockSet = new HashSet<>();
        for (ElementalBondsBlock elementalBondsBlock : elementalBondsBlockSet) {
            List<ElementalBondsBombType> bombTypeList = ElementalBondsBombLogic.getBombTypeList(elementalBondsBlock);
            if (!bombTypeList.isEmpty()
                    && elementalBondsBombList.stream().noneMatch(
                    elementalBondsBomb -> elementalBondsBomb.getX() == elementalBondsBlock.getX()
                            && elementalBondsBomb.getY() == elementalBondsBlock.getY()
                            && ((elementalBondsBomb.getElementalBondsBombType().getBigType() == ElementalBondsBombBigType.FRAME) == (elementalBondsBlock.getElementalBondsBlockType().getBigType() == ElementalBondsBlockBigType.FRAME)))) {
                canBoomBlockSet.add(elementalBondsBlock);
            }
        }
        return canBoomBlockSet;
    }

    public static List<List<ElementalBondsBlock>> getEmptyBlockList(int width, int height) {
        List<List<ElementalBondsBlock>> blockList = new ArrayList<>();
        for (int y = 0; y < height; y++) {
            blockList.add(new ArrayList<>());
            for (int x = 0; x < width; x++) {
                blockList.get(y).add(new ElementalBondsBlock(x, y, 0));
            }
        }
        return blockList;
    }

    /**
     * 方块是否相邻（可斜消时斜着的也算相邻）
     */
    public static boolean adjoin(ElementalBondsBlock elementalBondsBlock1, ElementalBondsBlock elementalBondsBlock2) {
        int subY = Math.abs(elementalBondsBlock1.getY() - elementalBondsBlock2.getY());
        int subX = Math.abs(elementalBondsBlock1.getX() - elementalBondsBlock2.getX());
        return subX + subY == 1;
    }

    public static List<Integer> getCanCreateColor(List<List<ElementalBondsBlock>> blockList, int colorCount, int x, int y) {
        Set<Integer> removeSet = new HashSet<>();
        // 下面2个颜色相同
        if (y - 2 >= 0
                && blockList.get(y - 1).get(x).getColor() == blockList.get(y - 2).get(x).getColor()) {
            removeSet.add(blockList.get(y - 1).get(x).getColor());
        }
        // 左边2个颜色相同
        if (x - 2 >= 0
                && blockList.get(y).get(x - 1).getColor() == blockList.get(y).get(x - 2).getColor()) {
            removeSet.add(blockList.get(y).get(x - 1).getColor());
        }
        // 结果
        List<Integer> colorList = new ArrayList<>();
        for (int i = 1; i <= colorCount; i++) {
            if (!removeSet.contains(i)) {
                colorList.add(i);
            }
        }
        return colorList;
    }

}
